package sf.dynamicsql;

import org.mybatis.dynamic.sql.AliasableSqlTable;
import org.mybatis.dynamic.sql.SqlColumn;
import sf.core.DBField;
import sf.core.DBObject;
import sf.database.dao.DBContext;
import sf.database.dialect.DBDialect;
import sf.database.meta.ColumnMapping;
import sf.database.meta.MetaHolder;
import sf.database.meta.TableMapping;
import sf.database.util.OrmUtils;
import sf.database.util.SimpleSQLTemplate;

import java.sql.JDBCType;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 动态表
 */
public class DynamicAliasSqlTable extends AliasableSqlTable<DynamicAliasSqlTable> {

    protected final Map<DBField, SqlColumn<?>> map = new LinkedHashMap<>();
    private Class<? extends DBObject> clz;
    private DBDialect dialect;
    /**
     * 是否使用模板替换关键字
     */
    private boolean useTemplateReplace;

    /**
     * @param clz
     * @param context
     * @param dialect
     * @param useTemplateReplace 是否使用模板替换关键字
     */
    public DynamicAliasSqlTable(Class<? extends DBObject> clz, DBContext context, DBDialect dialect, boolean useTemplateReplace) {
//        super((StringUtils.isNotBlank(MetaHolder.getMeta(clz).getCatalog()) ? () -> Optional.of(MetaHolder.getMeta(clz).getCatalog()) : Optional::empty),
//                (StringUtils.isNotBlank(MetaHolder.getMeta(clz).getSchema()) ? () -> Optional.of(MetaHolder.getMeta(clz).getSchema()) : Optional::empty),
//                getDialectName(OrmUtils.getDynamicTableName(context, MetaHolder.getMeta(clz)), dialect));
        super(getReplaceName(OrmUtils.getDynamicTableName(context, MetaHolder.getMeta(clz)), dialect, useTemplateReplace),
                () -> new DynamicAliasSqlTable(clz, context, dialect, useTemplateReplace));
        this.clz = clz;
        this.dialect = dialect;
        this.useTemplateReplace = useTemplateReplace;
        init(clz, dialect);
    }

    /**
     * @param context
     * @param dialect
     * @param clz
     */
    public DynamicAliasSqlTable(Class<? extends DBObject> clz, DBContext context, DBDialect dialect) {
        this(clz, context, dialect, false);
    }

    public DynamicAliasSqlTable(String tableName) {
        super(tableName,()->new DynamicAliasSqlTable(tableName));
    }

    /**
     * 重建字段,可以设置字段的特殊字符替换.
     */
    public void rebuild() {
        map.clear();
        TableMapping tm = MetaHolder.getMeta(clz);
        for (Map.Entry<DBField, ColumnMapping> entry : tm.getSchemaMap().entrySet()) {
            SqlColumn<?> sqlColumn = getSQLDataType(entry.getValue(), dialect, useTemplateReplace);
            map.put(entry.getKey(), sqlColumn);
        }
    }

    private void init(Class<? extends DBObject> clz, DBDialect dialect) {
        this.clz = clz;
//        this.schema = getSchema(clz, dialect);
        TableMapping tm = MetaHolder.getMeta(clz);
        for (Map.Entry<DBField, ColumnMapping> entry : tm.getSchemaMap().entrySet()) {
            SqlColumn<?> sqlColumn = getSQLDataType(entry.getValue(), dialect, useTemplateReplace);
            map.put(entry.getKey(), sqlColumn);
        }
    }

    private <T> SqlColumn<T> getSQLDataType(ColumnMapping cm, DBDialect dialect, boolean useTemplateReplace) {
        JDBCType type = JDBCType.valueOf(cm.getSqlType());
        return column(getReplaceName(cm.getRawColumnName(), dialect, useTemplateReplace), type);
    }

    private static String getReplaceName(String name, DBDialect dialect, boolean useTemplateReplace) {
        if (useTemplateReplace) {
            name = SimpleSQLTemplate.wrapperColumn(name);
        } else if (dialect != null && dialect.containKeyword(name)) {
            name = dialect.wrapKeyword(name);
        }
        return name;
    }

    /**
     * 获取列
     * @param field
     * @param <T>
     * @return
     */
    public <T> SqlColumn<T> column(DBField field) {
        SqlColumn<T> expression = (SqlColumn<T>) map.get(field);
        if (expression == null) {
            throw new RuntimeException("字段不存在");
        }
        return expression;
    }

    /**
     * 获取列,快速写法
     * @param field
     * @param <T>
     * @return
     */
    public <T> SqlColumn<T> c(DBField field) {
        return column(field);
    }

    /**
     * 获取列
     * @param field
     * @param <T>
     * @return
     */
    public <T> SqlColumn<T> columnNew(DBField field, DBDialect dialect, boolean useTemplateReplace) {
        SqlColumn<T> expression = null;
        TableMapping tm = MetaHolder.getMeta(clz);
        ColumnMapping cm = tm.getSchemaMap().get(field);
        if (cm != null) {
            expression = getSQLDataType(cm, dialect, useTemplateReplace);
        }
        if (expression == null) {
            throw new RuntimeException("字段不存在");
        }
        return expression;
    }

    /**
     * 获取列,快速写法
     * @param field
     * @param <T>
     * @return
     */
    public <T> SqlColumn<T> cNew(DBField field, DBDialect dialect, boolean useTemplateReplace) {
        return columnNew(field, dialect, useTemplateReplace);
    }


    public boolean isUseTemplateReplace() {
        return useTemplateReplace;
    }

    public void setUseTemplateReplace(boolean useTemplateReplace) {
        this.useTemplateReplace = useTemplateReplace;
    }

    public DBDialect getDialect() {
        return dialect;
    }

    public void setDialect(DBDialect dialect) {
        this.dialect = dialect;
    }

    public Map<DBField, SqlColumn<?>> getMap() {
        return map;
    }

    public Class<? extends DBObject> getClz() {
        return clz;
    }

    public void setClz(Class<? extends DBObject> clz) {
        this.clz = clz;
    }

    /**
     * 克隆
     * @return
     */
    public DynamicAliasSqlTable cloneNew() {
        return new DynamicAliasSqlTable(clz, null, dialect);
    }

    public DynamicAliasSqlTable cloneNew(DBContext context, DBDialect dialect) {
        return new DynamicAliasSqlTable(clz, context, dialect);
    }
}
